package com.hmdp.utils;

import cn.hutool.core.util.BooleanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hmdp.constant.GlobalConstant;
import com.hmdp.entity.dto.ExpirationTimeDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
public class MyUtil {
    @Autowired
    RedisTemplate redisTemplate;
    private boolean getLock(String key)
    {
        Boolean b = redisTemplate.opsForValue().setIfAbsent(key, "1",GlobalConstant.MUTEX_SHOP_ID_TTL,TimeUnit.SECONDS);
        return BooleanUtil.isTrue(b);
    }
    private void releaseLock(String key)
    {
        redisTemplate.delete(key);
    }
    public <T> void set(String key,T obj,Long ttl,TimeUnit timeUnit)
    {
        String jsonString = JSONObject.toJSONString(obj);
        redisTemplate.opsForValue().set(key,jsonString,ttl,timeUnit);
    }
    public <T> void set(String key,T obj)
    {
        String jsonString = JSONObject.toJSONString(obj);
        redisTemplate.opsForValue().set(key,jsonString);
    }
    public <T> void setWithLogicalExpiration(String key, T obj, Long ttl, TimeUnit timeUnit)
    {
        ExpirationTimeDTO expirationTimeDTO=new ExpirationTimeDTO();
        expirationTimeDTO.setObj(obj);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expirationTime = now.plusSeconds(timeUnit.toSeconds(ttl));
        expirationTimeDTO.setExpirationTime(expirationTime);
        String jsonString = JSONObject.toJSONString(expirationTimeDTO);
        redisTemplate.opsForValue().set(key,jsonString);
    }
    public <R> R  getWithLogicalExpiration(String key, Class<R> type, Supplier<Void> supplier)
    {
        String s =(String) redisTemplate.opsForValue().get(key);
        if(s==null)
        {
            return null;
        }
        ExpirationTimeDTO dto = JSON.parseObject(s, ExpirationTimeDTO.class);
        JSONObject jsonObject = (JSONObject) dto.getObj();
        R obj = jsonObject.toJavaObject(type);
        if(LocalDateTime.now().isAfter(dto.getExpirationTime()))
        {
            if(getLock(GlobalConstant.MUTEX_PREFIX+key))
            {
                 s =(String) redisTemplate.opsForValue().get(key);
                 dto = JSON.parseObject(s, ExpirationTimeDTO.class);
                 jsonObject = (JSONObject) dto.getObj();
                 obj = jsonObject.toJavaObject(type);
                if(LocalDateTime.now().isAfter(dto.getExpirationTime()))
                {
                    new Runnable() {
                        @Override
                        public void run() {
                            supplier.get();
                            releaseLock(GlobalConstant.MUTEX_PREFIX+key);
                        }
                    }.run();
                }
            }
        }
        return obj;
    }
    public <R> R getWithNull(String key,Class<R> type,Supplier<Void> supplier)
    {
        String s = (String)redisTemplate.opsForValue().get(key);
        if(s==null)
        {
            supplier.get();
            s = (String)redisTemplate.opsForValue().get(key);
            R r = JSON.parseObject(s, type);
            return r;
        }
        if(s.equals(""))
        {
            return null;
        }
        R r = JSON.parseObject(s, type);
        return r;
    }
}
